package org.goldenport.g3

import scala.xml.Node
import org.goldenport.g3.messages.Get
import org.goldenport.g3.events.RestEvent
import org.goldenport.g3.atom._

/**
 * @since   Apr. 29, 2010
 * @version Oct.  3, 2010
 * @author  ASAMI, Tomoharu
 */
trait G3Actionable extends G3Contextable {
  protected def parcelTitle: String

  protected final def execute_action(parcel: G3Parcel): G3Parcel = {
    try {
      do_Action(parcel)
    } catch {
      case e: Exception => {
        context.createExceptionParcel(parcelTitle)(e, parcel)
      }
    }
  }

  protected final def post_action(parcel: G3Parcel, content: AnyRef, attachments: List[AnyRef], ctx: G3AgentContext): G3Parcel = {
    if ((content == parcel.content) && (attachments == parcel.attachments)) {
      parcel
    } else {
      ctx.createContentAttachmentsParcel(parcelTitle)(content, attachments, parcel)
    }
  }

  protected def do_Action(parcel: G3Parcel): G3Parcel = {
    val ctx = G3AgentContext(parcel, context)
    val (content, attachments) = do_Action(parcel, ctx)
    post_action(parcel, content, attachments, ctx)
  }

  protected def do_Action(parcel: G3Parcel, ctx: G3AgentContext): (AnyRef, List[AnyRef]) = {
//    println("G3Actionable do_Action: " + parcel.content)
    val result = parcel.content match {
      case msg: G3Message => {
//        println("G3Actionable do_Action msg: " + msg.normalize(ctx))
        msg.normalize(ctx).head match {
          case control: G3Control => control.eval(
            msg => do_Action(msg, parcel, ctx)
          )
          case content => do_Action(content, parcel, ctx)
        }
      }
      case content => do_Action(content, parcel, ctx)
    }
    (result, parcel.attachments)
  }

  protected def do_Action(content: AnyRef, parcel: G3Parcel, ctx: G3AgentContext): AnyRef = {
    content
  }
}

trait ActivityActionable extends G3Actionable {
  protected def create_Activity(ctx: G3AgentContext): PartialFunction[AnyRef, AnyRef]

  override protected def do_Action(content: AnyRef, parcel: G3Parcel, ctx: G3AgentContext): AnyRef = {
//    println("ActivityActionable: do_Action: " + content)
    G3Actionable.do_action(create_Activity(ctx))(content, parcel, ctx) match {
      case Some(r) => r
      case None => content
    }
  }
}

object G3Actionable {
  def do_action(activity: PartialFunction[AnyRef, AnyRef])(content: AnyRef, parcel: G3Parcel, ctx: G3AgentContext): Option[AnyRef] = {
    def action_xml(content: AnyRef): Option[AnyRef] = {
      val xmlnodes: List[Node] = content match {
        case feed: AtomFeed if feed.length == 1 => {
          feed.entries(0).contentXml
        }
        case entry: AtomEntry => {
          entry.contentXml
        }
        case AtomFeed(feed) if feed.length == 1 => {
          feed.entries(0).contentXml
        }
        case _ => Nil
      }
      if (xmlnodes.isEmpty) {
        None
      } else {
        if (activity.isDefinedAt(xmlnodes)) {
          Some(activity(xmlnodes))
        } else if (xmlnodes.length == 1) {
          val xmlnode = xmlnodes(0)
          if (activity.isDefinedAt(xmlnode)) {
            Some(activity(xmlnode))
          } else {
            None
          }
        } else {
          None
        }
      }
    }

    def action_content(content: AnyRef): Option[AnyRef] = {
//      println("action_content in: " + content)
      content match {
        case get: Get => {
//          println("action_content get = " + get.content)
          get.content.flatMap(activity.lift) orElse
          get.content.flatMap(action_xml)
        }
        case rest: RestEvent => {
/*
          println("action_content rest = " + rest.content)
          val x = rest.content match {
            case Some(c) => if (activity.isDefinedAt(c)) {
              println("action_content = " + activity(c))
              Some(activity(c))
            } else {
              None
            }
            case None => None
          }
          x orElse
*/
          rest.content.flatMap(activity.lift) orElse
          rest.content.flatMap(action_xml)
/*
          if (activity.isDefinedAt(get.contents)) {
            Some(activity(get.contents))
          } else if (!get.contents.exists(!activity.isDefinedAt(_))) {
            Some(
              get.contents.map(activity) match {
                case List(a) => a
                case list => list
              }
            )
          } else None
*/
        }
        case _ => None
      }
    }

//    println("G3Actionable activity: " + content)

    activity.lift(content) orElse
    activity.lift(parcel) orElse
    action_content(content) orElse
    action_xml(content)
  }
}
